
#include "BeastHttpServer.h"
#include <thread>

namespace http = boost::beast::http;
namespace asio = boost::asio;

using tcp = asio::ip::tcp;

bool BeastHttpServer::StartServer(uint16_t port, int thread_num)
{
    if (_IsServerStarted)
    {
        return false;
    }

    _IsServerStarted = true;

    beast::error_code ec;
    _acc.open(tcp::v4(), ec);
    if (ec)
    {
         return false;
    }

    _acc.set_option(asio::socket_base::reuse_address(true), ec);
    if (ec)
    {
        return false;
    }

    tcp::endpoint ep(asio::ip::make_address("0.0.0.0"), port);
    _acc.bind(ep, ec);
    if (ec)
    {
        return false;
    }

    _acc.listen(asio::socket_base::max_connections, ec);
    if (ec)
    {
        return false;
    }

    auto self_ptr = shared_from_this();
    asio::spawn(_ioc, std::bind(&BeastHttpServer::do_listen, self_ptr, std::placeholders::_1));

    auto& ioc_ref = _ioc;

    while (thread_num-- > 0 )
    {
        std::thread thr([self_ptr, &ioc_ref]() {
            ioc_ref.run();
        });
        thr.detach();
    }

    return true;
}

void BeastHttpServer::SetRequestHandler(std::shared_ptr<BeastHttpServerRequestHandler> requestHandler)
{
    _requestHandler = requestHandler;
}

void BeastHttpServer::do_listen(boost::asio::yield_context yield)
{
    boost::beast::error_code ec;

    while (true)
    {
        tcp::socket sock{ _ioc };
        _acc.async_accept(sock, yield[ec]);
        if (ec)
        {
            return;
        }

        boost::asio::spawn(_ioc, std::bind(&BeastHttpServer::do_session, shared_from_this(), beast::tcp_stream(std::move(sock)), std::placeholders::_1));
    }
}

void BeastHttpServer::do_session(boost::beast::tcp_stream& stream, boost::asio::yield_context yield)
try
{
    boost::beast::error_code ec;

    boost::beast::flat_buffer buffer;

    for (;;)
    {
        // Set the timeout.
        stream.expires_after(std::chrono::seconds(100));

        // Read a request
        http::request<http::string_body> req;
        http::async_read(stream, buffer, req, yield[ec]);
        if (ec == http::error::end_of_stream)
        {
            break;
        }

        if (ec)
        {
            //TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_ERROR, "async_readʧ��, err: %s", ec.message().c_str());
            return;
        }

        http::response<http::string_body> res; //�ظ�

        if (_requestHandler != nullptr)
        {
            if (_requestHandler->handle_request(req, res)) //�ɹ�����
            {
                http::response_serializer<http::string_body> sr{ res };
                http::async_write(stream, sr, yield[ec]);
                if (ec)
                {
                    //TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_ERROR, "async_writeʧ��, err: %s", ec.message().c_str());
                    break;
                }

                break;
            }
            else
            {
                //TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_ERROR, "handle_request failed");
                break;
            }
        }
        else
        {
            //TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_ERROR, "_requestHandler == nullptr");
            break;
        }
    }

    stream.socket().shutdown(tcp::socket::shutdown_send, ec);
}
catch (const std::exception & e)
{
    //TRACE_LOG_A(nullptr, TRACE_LOG_LEVEL_ERROR, "exception %s", e.what() );
}